package com.xinlianshiye.im.websocket;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xinlianshiye.im.RequestEnum;
import com.xinlianshiye.im.RequestBody;
import com.xinlianshiye.clouds.common.exception.WebMessageExceptionBuilder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestParam;

import javax.websocket.Session;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

public class WebSocketHandlerMethod {

    private final Object bean;
    private final Method method;
    private final WebMessageExceptionBuilder exceptionBuilder;
    private static final ObjectMapper mapper = new ObjectMapper();

    public WebSocketHandlerMethod(Object bean, Method method, WebMessageExceptionBuilder exceptionBuilder) {
        this.bean = bean;
        this.method = method;
        this.exceptionBuilder = exceptionBuilder;
    }

    public Object invoke(Session session, String payload) throws IOException, InvocationTargetException, IllegalAccessException {
        StringBuilder notNull = new StringBuilder();
        List<Object> params = new ArrayList<>();
        RequestBody httpRequestBody = mapper.readValue(payload, RequestBody.class);
        for (Parameter parameter : method.getParameters()) {
            if (parameter.getName().equals("messageBody") && parameter.getType().equals(RequestBody.class)) {
                params.add(httpRequestBody);
                continue;
            }
            if (parameter.getName().equals("session") && parameter.getType().equals(Session.class)) {
                params.add(session);
                continue;
            }
            RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
            String name = parameter.getName();
            boolean required = false;
            if (requestParam != null) {
                if (StringUtils.hasText(requestParam.name())) {
                    name = requestParam.name();
                }
                if (StringUtils.hasText(requestParam.value())) {
                    name = requestParam.value();
                }
                required = requestParam.required();
            }
            JsonNode jsonNode = mapper.readTree(httpRequestBody.getContent()).get(name);
            Class<?> parameterType = parameter.getType();
            if (jsonNode == null) {
                if (required) {
                    if (notNull.length() > 0) {
                        notNull.append(",");
                    }
                    notNull.append(name);
                }
                params.add(null);
            } else if (parameterType.equals(Integer.class)) {
                params.add(jsonNode.asInt());
            } else if (parameterType.equals(Long.class)) {
                params.add(jsonNode.asLong());
            } else if (parameterType.equals(Double.class)) {
                params.add(jsonNode.asDouble());
            } else if (parameterType.equals(Boolean.class)) {
                params.add(jsonNode.asBoolean());
            } else if (parameterType.equals(String.class)) {
                params.add(jsonNode.asText());
            } else if (parameterType.equals(BigInteger.class)) {
                params.add(jsonNode.bigIntegerValue());
            } else if (parameterType.equals(BigDecimal.class)) {
                params.add(jsonNode.decimalValue());
            } else if (parameterType.equals(int.class)) {
                params.add(jsonNode.intValue());
            } else if (parameterType.equals(short.class)) {
                params.add(jsonNode.shortValue());
            } else if (parameterType.equals(long.class)) {
                params.add(jsonNode.longValue());
            } else if (parameterType.equals(boolean.class)) {
                params.add(jsonNode.booleanValue());
            } else if (parameterType.equals(float.class)) {
                params.add(jsonNode.floatValue());
            } else if (parameterType.equals(double.class)) {
                params.add(jsonNode.doubleValue());
            } else if (parameterType.isArray()) {
                params.add(jsonNode.withArray(""));
            } else {
                params.add(mapper.readValue(jsonNode.traverse(), parameterType));
            }
        }
        if (notNull.length() > 0) {
            throw exceptionBuilder.build(0, RequestEnum.PARAMTERNONNULL + ":" + notNull.toString());
        }
        return method.invoke(bean, params.toArray());
    }

    public void invoke() {
        try {
            Object invoke = method.invoke(bean);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        String content = "{\"start\":0,\"limit\":30, \"items\":{\"start\":0}, \"total\":null}";
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = mapper.readTree(content);
        int start = jsonNode.get("start").asInt();
        long limit = jsonNode.get("limit").asLong();
        System.out.println(start);
        System.out.println(limit);
        System.out.println(jsonNode.get("start").getNodeType());
        System.out.println(jsonNode.with("items").getClass());
        System.out.println(jsonNode.get("items").getNodeType());
        System.out.println(jsonNode.get("total").getNodeType());
        Object o = mapper.readValue(jsonNode.traverse(), Object.class);
        System.out.println(o);
        System.out.println(o.getClass());
    }
}
